package com.heima.account.handler;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.commons.enums.BusinessErrors;
import com.heima.commons.exception.BusinessRuntimeException;
import com.heima.modules.po.VehiclePO;
import okhttp3.*;
import org.apache.commons.io.FileUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

@Component
public class AiHelper {

    @Value("${baidu.apikey}")
    private String API_KEY;
    @Value("${baidu.secretkey}")
    private String SECRET_KEY;

    private final static Logger logger = LoggerFactory.getLogger(AiHelper.class);

      //：创建一个 OkHttpClient 对象，并通过 Builder 模式进行构建。
      static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build();


    public static void main(String []args) throws IOException {

        String code = new AiHelper().getLicense(null);
        System.out.println(code);
    }

    /*

    图像识别，获取车牌信息
    文档（行驶证识别）：https://cloud.baidu.com/doc/OCR/s/yk3h7y3ks
    文档（车牌识别）：https://cloud.baidu.com/doc/OCR/s/ck3h7y191
    获取车辆照片url
    将url下载到某个临时文件夹
    将文件编码为base64
    调百度AI接口，返回对应信息
    对比：行驶证车牌 和 车辆车牌是否一致
    如果一致，设置车牌信息，认证通过，身份变更为车主

    简化版业务流程（至少完成）：识别车辆车牌号即可

    * */
    public String getLicense(VehiclePO vehiclePO) throws IOException {
        String frontPhoto = vehiclePO.getCarFrontPhoto();
        //new File(AiHelper.class.getResource("/").getPath())就是根据AiHelper类的类加载器根路径所对应的文件系统路径，创建一个File对象。
        // 这个File对象可以用来进行文件操作，如读写文件、获取文件属性等
        //AiHelper.class.getResource("/")：获取当前项目根目录的URL路径。例如，如果项目部署在Tomcat服务器上，可能获取到的是类似file:/path/to/project/这样的路径
        //.getPath()：将URL路径转换为标准的文件系统路径格式
        //frontPhoto.substring(frontPhoto.lastIndexOf("."))获取图片URL（frontPhoto）的文件扩展名。例如，如果车辆ID为123，图片URL为http://example.com/image.jpg，则拼接后的文件名为front-123.jpg
        File file = new File(AiHelper.class.getResource("/").getPath()+"front-"+vehiclePO.getId()+frontPhoto.substring(frontPhoto.lastIndexOf(".")), String.valueOf(frontPhoto.length()));
        logger.info("create file:{}",file.getAbsolutePath());
        //FileUtils是Apache Commons IO库中的一个工具类，提供了一系列用于文件操作的静态方法。这些方法简化了文件的读取、写入、复制等操作
        //copyURLToFile是FileUtils类中的一个方法，用于将一个URL指向的内容复制到一个本地文件
        //URL是Java标准库中的一个类，用于表示统一资源定位符（URL）。
        //URL是Java标准库中的一个类，用于表示统一资源定位符（URL）,new URL(frontPhoto)创建了一个URL对象，frontPhoto是一个字符串，表示要复制的资源的URL地址。例如，frontPhoto可以是一个图片的URL，如"https://example.com/image.jpg"
        FileUtils.copyURLToFile(new URL(frontPhoto), file);
        // 在 OkHttp3 中，post 请求默认的类型是：application/x-www-form-urlencoded
        //Content-Type: application/x-www-form-urlencoded 是HTTP协议中用于表单数据提交的一种编码类型
        //Content-Type 是HTTP请求头中的一个字段，用于指示请求体（body）中的数据类型。它告诉服务器客户端发送的数据是什么格式，以便服务器能够正确解析这些数据。
        //application/x-www-form-urlencoded 是一种常见的表单数据编码类型，主要用于HTML表单的提交。
        //当表单的enctype属性设置为application/x-www-form-urlencoded时，表单数据会以键值对的形式进行编码，并通过URL编码（也称为百分号编码）进行处理
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        // image 可以通过 getFileContentAsBase64("C:\fakepath\OIP-C.jpg") 方法获取,如果Content-Type是application/x-www-form-urlencoded时,第二个参数传true
        String image=getFileContentAsBase64(file.getAbsolutePath(),true);
        //RequestBody.create 方法用于创建一个请求体。第一个参数 mediaType 是媒体类型，用于指定请求体内容的格式，例如 MediaType.parse("application/x-www-form-urlencoded") 表示表单编码格式，MediaType.parse("image/jpeg") 表示JPEG图片格式等
        //"image="+image 是请求体的内容，这里是将一个名为 image 的变量的值拼接到字符串 "image=" 后面，作为请求体的内容发送给服务器
        RequestBody body = RequestBody.create(mediaType, "image="+image);
        //构建一个Request对象。通过Request.Builder设置请求的URL为https://aip.baidubce.com/oauth/2.0/token，请求方法为POST，
        // 并使用之前创建的RequestBody对象作为请求体。同时，添加了Content-Type头信息，指定为application/x-www-form-urlencoded。
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate?access_token=" + getAccessToken())
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        //使用HTTP_CLIENT发送构建好的请求，并执行获取响应。
        Response response = HTTP_CLIENT.newCall(request).execute();
        //将response返回体转为json字符串
        String json = response.body().string();
        logger.info("get response from baidu AI:{}",json);
        int index=json.indexOf("号牌号码");
        //ObjectMapper是Jackson库中的一个类，用于处理JSON数据的序列化和反序列化。它提供了多种方法来读取和写入JSON数据
        ObjectMapper objectMapper = new ObjectMapper();
        //readTree(json)是ObjectMapper类的一个方法，用于将JSON字符串解析为一个JsonNode对象。JsonNode是Jackson库中的一个类，表示JSON树结构中的一个节点。
        //参数：json：一个字符串，表示要解析的JSON数据
        JsonNode rootNode = objectMapper.readTree(json);
        //rootNode是一个JsonNode对象，表示JSON树的根节点。get("error_code")方法用于获取名为"error_code"的子节点。如果该节点存在且不为null，表示请求返回了错误代码
        if (rootNode.get("error_code") != null){
            throw new BusinessRuntimeException(BusinessErrors.DATA_STATUS_ERROR,rootNode.get("error_msg").asText());
        }
        if(file.exists()){
            file.delete();
        }
        //rootNode.get("words_result")获取名为"words_result"的子节点。get("number")获取"words_result"节点下的"number"子节点。asText()方法将该节点的值转换为字符串
        String text = rootNode.get("word_result").get("number").asText();
        logger.info("result text:{}",text);
        return text;


    }

    public  String getFileContentAsBase64(String file,boolean urlEncode)throws IOException {
        //字节（byte）：是计算机中最小的存储单位之一，占8位二进制。它可以表示的数值范围是从-128到127（有符号整数）。在Java中，byte 是一种基本数据类型
        //存储二进制数据：byte[] 可以存储任意的二进制数据。由于字节是计算机存储的基本单位，因此它可以用来表示各种类型的数据，如文本、图片、音频等。
        //适合处理大量数据：当需要处理大量数据时，byte[] 是一个非常高效的选择。例如，在读取文件、网络传输等场景中，数据通常以字节流的形式出现，使用 byte[] 可以方便地存储和操作这些数据。
        //与I/O操作紧密相关：在Java的输入输出（I/O）操作中，byte[] 起到了核心作用。例如，InputStream 和 OutputStream 类及其子类在读取和写入数据时，通常会使用 byte[] 作为缓冲区。

        //Files类是Java NIO包中的一个工具类，提供了一组用于文件和文件系统操作的静态方法。
        //readAllBytes(Path path)方法用于读取指定路径（Path对象）的文件内容，并将其存储到一个byte[]数组中。该方法会读取文件的全部字节内容，直到文件末尾
        //如果文件内容超过Integer.MAX_VALUE（即文件大小超过2GB），该方法会抛出OutOfMemoryError异常，因为byte[]数组的最大长度是Integer.MAX_VALUE。该方法还会抛出IOException异常，如果在读取文件过程中发生I/O错误，例如文件不存在、无法读取等

        //Paths类也是Java NIO包中的一个工具类，提供了一组用于创建Path对象的静态方法。
        //get(String first, String... more)方法用于创建一个Path对象。first参数是路径的起始部分，more参数是路径的后续部分。如果more参数为空，则first参数可以是绝对路径或相对路径。
        //在这段代码中，file是一个字符串，表示文件的路径。Paths.get(file)会将这个字符串转换为一个Path对象，该对象封装了文件的路径信息
        byte[] photo= Files.readAllBytes(Paths.get(file));
        //getEncoder()方法用于获取一个Base64编码器实例。这个方法返回一个Base64.Encoder对象，该对象提供了编码功能
        //encodeToString(byte[] src)方法用于将字节数组src编码为一个Base64字符串。参数src是一个字节数组，可以是任何二进制数据，例如文件内容、加密数据等。
        // 该方法返回一个字符串，该字符串是src字节数组的Base64编码表示
        String base64=Base64.getEncoder().encodeToString(photo);
        if(urlEncode){
            //URLEncoder类是Java标准库中的一个类，提供了一组用于将字符串进行URL编码的静态方法。URL编码（也称为百分比编码）是一种将字符串转换为适合在URL中传输的格式的编码方式。它将特殊字符（如空格、斜杠、冒号等）转换为%加两位十六进制数的形式，例如空格会被编码为%20
            //encode(String s, String enc)方法用于将字符串s按照指定的字符集enc进行URL编码。该方法返回一个字符串，该字符串是s的URL编码表示,如果指定的字符集无效，该方法会抛出UnsupportedEncodingException异常
            //参数s是要编码的字符串，可以是任何文本内容。参数enc是字符集名称，用于指定编码时使用的字符集。常见的字符集有"UTF-8"、"ISO-8859-1"等
             base64=URLEncoder.encode(base64,"UTF-8");
        }
        return base64;
    }

    public String getAccessToken() throws IOException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY
                + "&client_secret=" + SECRET_KEY);
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/oauth/2.0/token")
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        //使用HTTP_CLIENT发送构建好的请求，并执行获取响应。
        Response response = HTTP_CLIENT.newCall(request).execute();
        //将响应体的字符串内容转换为JSONObject对象，并从中获取access_token的值并返回
        return new JSONObject(response.body().string()).getString("access_token");

    }



}
